/*
 *  Queen Mary University of London MOM System
 *  
 *  Copyright Queen Mary University of London
 *  
 *  Authors:
 *  Bob Chew (bob.chew@elec.qmul.ac.uk)
 *  Beatriz Viñal Murciano (beavimu@gmail.com)
 *  John Bigham (john.bigham@elec.qmul.ac.uk)
 *  
 *  
 *  Portions of this software uses Apache Qpid
 *    http://qpid.apache.org
 *  
 *  Apache Qpid license:
 * --------------------------------------------------------------
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * -------------------------------------------------------------
 * 
 */

package momControlClient;

import java.util.UUID;

import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.TopicSubscriber;

import org.apache.qpid.AMQException;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.AMQTopic;
import org.apache.qpid.client.TopicPublisherAdapter;
import org.apache.qpid.client.message.AbstractJMSMessage;
import org.apache.qpid.client.message.JMSTextMessage;
import org.apache.qpid.url.URLSyntaxException;

/**
 * Generic Qpid client for the broker failure detection system.
 * The clients in the system connect to only ONE broker, so the only connection
 * and session they have can be an attribute of the class.
 * Furthermore, only Detector.java, Controller.java and OverlayManager.java
 * extend QpidClient, and they all connect to the broker controller.
 * I'll let the broker to connect to as a parameter to remember to which broker
 * a connection is being set, but I'll hardcode the port (always 5672).
 */
public class MOMControlClient {

    /** Unique ID of the client. */
    private final String clientID;

    /** Username. */
    private static final String USERNAME = "guest"; //TODO: not always the same
    /** Password. */
    private static final String PASSWORD = "guest"; //TODO: not always the same

    /** Name of the virtual host. */
    private static final String VIRTUAL_HOST = "news";
    /** An exchange that already exists is used. */
    private static final String EXCHANGE = "amq.topic";

    /** Connection to the broker to publish messages to. */
    private AMQConnection connection;
    /** Session in connection. */
    private AMQSession session;

    /**
     * Getter for the variable 'clientID'.
     * @return variable 'clientID'
     */
    public String getClientID() {
        return clientID;
    }
    /**
     * Getter for the variable 'USERNAME'.
     * @return variable 'USERNAME'
     */
    public String getUSERNAME() {
        return USERNAME;
    }
    /**
     * Getter for the variable 'PASSWORD'.
     * @return variable 'PASSWORD'
     */
    public String getPASSWORD() {
        return PASSWORD;
    }
    /**
     * Getter for the variable 'VIRTUAL_HOST'.
     * @return variable 'VIRTUAL_HOST'
     */
    public String getVIRTUAL_HOST() {
        return VIRTUAL_HOST;
    }
    /**
     * Getter for the variable 'EXCHANGE'.
     * @return variable 'EXCHANGE'
     */
    public String getEXCHANGE() {
        return EXCHANGE;
    }
    /**
     * Getter for the variable 'connection'.
     * @return variable 'connection'
     */
    public AMQConnection getConnection() {
        return connection;
    }
    /**
     * Setter for the variable 'connection'.
     * @param connection  new value of the variable 'connection'
     */
    public void setConnection(AMQConnection connection) {
        this.connection = connection;
    }
    /**
     * Getter for the variable 'session'.
     * @return variable 'session'
     */
    public AMQSession getSession() {
        return session;
    }
    /**
     * Setter for the variable 'session'.
     * @param session  new value of the variable 'session'
     */
    public void setSession(AMQSession session) {
        this.session = session;
    }

    /**
     * Constructor.
     */
    public MOMControlClient() {
        //get a unique ID
        clientID = UUID.randomUUID().toString();
    }

    /**
     * Create and start a connection and create a session in it.
     *
     * @param brokerIP  IP address of the broker we want to connect to
     * @throws AMQException  if there are problems
     * @throws URLSyntaxException  if there are problems
     * @throws JMSException  if there are problems
     */
    protected void openConnAndSess(final String brokerIP)
            throws URLSyntaxException, AMQException, JMSException {

        //create a connection
        connection = new AMQConnection(brokerIP, USERNAME, PASSWORD,
                                       clientID, VIRTUAL_HOST);
        //start the connection
        connection.start();

        //create a session, non transacted and auto acknowledged
        session = (AMQSession)
                connection.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);

    }

    /**
     * Create a publisher to the given topic.
     *
     * @param topicName  the topic to create a publisher to
     * @return  a publisher to the given topic
     * @throws JMSException  if there are problems
     */
    protected TopicPublisherAdapter createPublisher(final String topicName)
            throws JMSException {
        //create topicURL
        String tURL = "topic://" + EXCHANGE + "/?routingkey='" + topicName
                      + "'&exclusive='true'&autodelete='true'";
        //create topic
        AMQTopic topic = (AMQTopic) session.createTopic(tURL);

        //return publisher
        return (TopicPublisherAdapter) session.createPublisher(topic);
    }

    /**
     * Create a subscriber to the given topic.
     *
     * @param topicName  topic to create the subscriber to
     * @return  a subscriber to the given topic
     * @throws JMSException  if there are problems
     */
    protected TopicSubscriber createSubscriber(final String topicName)
            throws JMSException {
        //create topicURL
        String tURL = "topic://" + EXCHANGE + "/?routingkey='" + topicName
                      + "'&exclusive='true'&autodelete='true'";
        //create topic
        AMQTopic topic = (AMQTopic) session.createTopic(tURL);

        //get a random subscription id
        String subscriptionId = UUID.randomUUID().toString();
        //return a subscriber
        return session.createDurableSubscriber(topic, subscriptionId);
    }

    /**
     * Create a text message with the given text and make the given publisher
     * send it.
     *
     * @param publisher  the publisher who will send the message
     * @param text  text of the message
     * @throws JMSException  if there are problems
     */
    protected void sendMessage(final TopicPublisherAdapter publisher,
                               final String text) throws JMSException {
        //create the message to send
        JMSTextMessage txtMessage =
                (JMSTextMessage) session.createTextMessage(text);
        //send the message
        publisher.publish(txtMessage, DeliveryMode.PERSISTENT,
                          AbstractJMSMessage.DEFAULT_PRIORITY,
                          AbstractJMSMessage.DEFAULT_TIME_TO_LIVE);
    }

    /**
     * Close the connection.
     *
     * @throws JMSException  if there are problems
     */
    protected void closeConnection() throws JMSException {
        connection.close();
    }

}
